Detector.addGetWebGLMessage() if not Detector.webgl
objects = []

width = window.innerWidth
height = window.innerHeight

scene = new THREE.Scene()
scene.fog = new THREE.Fog 0xcce0ff, 500, 10000

# renderer
renderer = new THREE.WebGLRenderer
			antialias: true
renderer.setClearColor scene.fog.color
renderer.setSize width, height
renderer.shadowMapEnabled = true


$('#canvas').appendChild renderer.domElement

DISTANCE = 1000
ROTATE_SPEED = 10
PI = 3.1415926

camera = new THREE.PerspectiveCamera 45, width / height, 1, 10000
camera.position.set DISTANCE, DISTANCE, DISTANCE
camera.lookAt new THREE.Vector3()


vector = new THREE.Vector3()
raycaster = new THREE.Raycaster()

# cube
cubeGeo = new THREE.BoxGeometry 50, 50, 50
materials =
	codeCube: () ->
		new THREE.MeshLambertMaterial
				color: 0xfeb74c
				ambient: 0xfeb74c
				shading: THREE.FlatShading
				map: THREE.ImageUtils.loadTexture 'textures/square-outline-textured.png'
	lamb: () ->
		new THREE.MeshLambertMaterial
				color: 0x999999
				shading: THREE.FlatShading
	engine: () ->
		 new THREE.MeshLambertMaterial
				color: 0xffffff
				shading: THREE.FlatShading
				map: THREE.ImageUtils.loadTexture 'textures/sprite.png'

# ground
groundTexture = THREE.ImageUtils.loadTexture 'textures/grasslight-big.jpg'
groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping
groundTexture.repeat.set 25, 25
groundTexture.anisotropy = 16

groundMaterial = new THREE.MeshPhongMaterial
					color: 0xffffff
					specular: 0x111111
					map: groundTexture

geometry = new THREE.PlaneBufferGeometry 20000, 20000
geometry.applyMatrix new THREE.Matrix4().makeRotationX(-Math.PI / 2)

plane = new THREE.Mesh geometry, groundMaterial
plane.receiveShadow = true

scene.add plane
objects.push plane

# roll-over helpers
rollOverGeo = new THREE.BoxGeometry 50, 50, 50
rollOverMaterial = new THREE.MeshBasicMaterial
					color: 0xff0000
					opacity: 0.5
					transparent: true
rollOverMesh = new THREE.Mesh rollOverGeo, rollOverMaterial
rollOverMesh.visible = false
scene.add rollOverMesh

# light
ambientLight = new THREE.AmbientLight 0x606060
scene.add ambientLight

directionalLight = new THREE.DirectionalLight 0xffffff
directionalLight.position.set 1, 0.75, 0.5
	.normalize()

# directionalLight.shadowCameraVisible = true
directionalLight.castShadow = true
directionalLight.shadowMapWidth = 1024
directionalLight.shadowMapHeight = 1024
# d = 5000
# directionalLight.shadowCameraLeft = -d
# directionalLight.shadowCameraRight = d
# directionalLight.shadowCameraTop = d
# directionalLight.shadowCameraBottom = -d
directionalLight.shadowCameraFar = 500
directionalLight.shadowCameraNear = -500

scene.add directionalLight

render = () ->
	requestAnimationFrame render
	renderer.render scene, camera


render()

# rotatable
RotatableEl = (controller) ->
	controller = document.body if not controller
	self = this
	this.size = 1.0
	this.mouseDown = false
	this.mouseMove = false
	this.shiftDown = false
	this.xDeg = this.yDeg = 45
	this._mouseLoc =
		x: 0
		y: 0

	this.update()


	document.addEventListener 'keydown', (e) ->
			if e.keyCode is 16
				self.shiftDown = true
		, false
	document.addEventListener 'keyup', (e) ->
			if e.keyCode is 16
				self.shiftDown = false
		, false

	controller.addEventListener 'mousedown', (e) ->
		e.preventDefault()
		self.mouseDown = true
		self.mouseMove = false
		self._mouseLoc = _.getMouseLoc e

	controller.addEventListener 'mousemove', (e) ->
		self.mouseMove = true
		Port.hide()
		if not (currentMode in Object.keys materials)
			rollOverMesh.visible = false
		else
			rollOverMesh.visible = true
			
		if not self.mouseDown
			vector.set(
				(e.clientX / width) * 2 - 1,
				-(e.clientY / height) * 2 + 1,
				0.5
				)

			vector.unproject camera

			raycaster.ray.set(
				camera.position,
				vector.sub(camera.position).normalize()
				)

			intersects = raycaster.intersectObjects objects

			if intersects.length > 0
				intersect = intersects[0]
				if currentMode is 'select' and intersect.object isnt plane
					surf = checkSurfBasedVector intersect.face.normal
					Port.show surf, e

				rollOverMesh.position.copy intersect.point
					.add intersect.face.normal
				rollOverMesh.position.divideScalar 50
					.floor()
					.multiplyScalar 50
					.addScalar 25
		else
			mouseLoc = _.getMouseLoc e
			self.rotateX self._mouseLoc.x - mouseLoc.x
			self.rotateY mouseLoc.y - self._mouseLoc.y
			self._mouseLoc = _.getMouseLoc e

	controller.addEventListener 'mouseup', (e) ->
		e.preventDefault()
		if not (self.mouseDown and self.mouseMove)
			vector.set(
				(e.clientX / width) * 2 - 1,
				-(e.clientY / height) * 2 + 1,
				0.5
				)

			vector.unproject camera

			raycaster.ray.set(
				camera.position,
				vector.sub(camera.position).normalize()
				)

			intersects = raycaster.intersectObjects objects

			if intersects.length > 0
				intersect = intersects[0]
				Menu.hide()
				if self.shiftDown
					if intersect.object isnt plane
						scene.remove intersect.object
						objects.splice objects.indexOf(intersect.object), 1
						originBlockArray.splice originBlockArray.indexOf(intersect.object.originCube), 1
						intersect.object.originCube.target = null
						intersect.object.originCube = null
				else
					if not (currentMode in Object.keys materials)
						if intersect.object != plane
							switch currentMode
								when 'select'
									Menu.show intersect.object.originCube

					else
						voxel = new THREE.Mesh cubeGeo, materials[currentMode]()
						voxel.position.copy intersect.point
							.add intersect.face.normal
						voxel.position.divideScalar 50
							.floor()
							.multiplyScalar 50
							.addScalar 25
						voxel.castShadow = true
						voxel.receiveShadow = true

						originBlock = null
						if currentMode is 'codeCube'
							originBlock = new Cube voxel
						if currentMode is 'engine'
							originBlock = new CubeEngine voxel
						if currentMode is 'lamb'
							originBlock = new CubeLight voxel
						originBlockArray.push originBlock
						voxel.originCube = originBlock

						scene.add voxel
						objects.push voxel

						calculateConnectionBasedPosition voxel.originCube


		self.mouseDown = false
		self.mouseMove = false

_.extend RotatableEl.prototype,
	update: () ->
		x = DISTANCE * Math.sin this.xDeg
		y = DISTANCE / 2 + this.yDeg
		z = DISTANCE * (Math.cos this.xDeg)
		camera.position.set x, y, z
		camera.lookAt new THREE.Vector3()

	setXDeg: (deg) ->
		this.xDeg = deg
		this.update()

	setYDeg: (deg) ->
		deg = -400 if deg < -400
		this.yDeg = deg
		this.update()

	rotateX: (deg) ->
		this.setXDeg this.xDeg + deg * ROTATE_SPEED * PI / 180 / 10

	rotateY: (deg) ->
		this.setYDeg this.yDeg + deg * ROTATE_SPEED

new RotatableEl $('#canvas')

# port
Port =
	list: ['一', '二', '三', '四', '五', '六']
	show: (id, e) ->
		$('#port').style.display = 'block'
		$('#port').innerHTML = "端口#{Port.list[id]}"
		$('#port').style.left = _.getMouseLoc(e).x + 'px'
		$('#port').style.top = _.getMouseLoc(e).y + 'px'
	hide: () ->
		$('#port').style.display = 'none'


# menu
Menu = {}
Menu.show = (cube) ->
	$('#prop').innerHTML = ''
	$('#menu').style.left = '0px'
	Menu.cube = cube
	switch cube.type
		when Cube.type.LIGHT
			$('#prop').innerHTML += '<h1 class="title">灯</h1>'
			$('#prop').innerHTML += '<p>六个面均可接受输入。当任意一个面有大于 0 的输入时灯即发光。</p>'
		when Cube.type.ENGINE
			$('#prop').innerHTML += '<h1 class="title">引擎</h1>'
			$('#prop').innerHTML += '<p>用于带动相连的方块进行运动。</p>'
			$('#prop').innerHTML += '<p>待完成。</p>'
		when Cube.type.CODE
			$('#prop').innerHTML += '<h1 class="title">处理器</h1>'
			$('#prop').innerHTML += '<p>六个面对应六个端口，可分别设置为输入(IN)、输出(OUT)和断开(UNDEFINED)。在程序中对应 this.port[0] 到 this.port[5]。</p>'
			$('#prop').innerHTML += '<p>可通过编写程序来读取或控制六个端口输入输出的值。</p>'
			$('#prop').innerHTML += '<p>将鼠标移到方块上可查看各个面对应的端口号。</p>'
			for value, index in Port.list
				select = document.createElement 'select'
				select.id = "surface#{index}"
				select.name = "surface#{index}"
				for key of Surface.type
					option = document.createElement 'option'
					option.value = Surface.type[key]
					option.innerText = key

					if cube.surfaces[index].info.io is Surface.type[key]
						option.setAttribute 'selected', 'selected'

					select.appendChild option
				label = document.createElement 'label'
				label.innerHTML = "端口#{value}: "
				label.appendChild select
				$('#prop').appendChild label
			$('#prop').innerHTML += '<div class="section">代码: </div>'
			code = document.createElement 'textarea'
			code.id = 'code'
			code.value = cube.code
			code.placeholder = '// 示例\n this.port[1] = this.port[0] + 1'
			$('#prop').appendChild code

Menu.hide = () ->
	if Menu.cube
		switch Menu.cube.type
			when Cube.type.LIGHT
				console.log 'light'
			when Cube.type.CODE
				for value, index in Port.list
					select = $("#surface#{index}")
					Menu.cube.surfaces[index].info.io = parseInt select.value

				Menu.cube.code = $('#code').value  if $('#code')
	$('#menu').style.left = '-400px';

#Richard vars
originBlockArray = []
currentMode = 'select'
isGaming = false
runtime = new window.Runtime()
gameCallback = (err, roundCount, data) ->
	console.log "----------- #{roundCount} stop"
	console.log data
	if err
		console.log err
		return

	for cube in data.lightOn
		console.log 'lightOn'
		cube.target.material.color.setHex 0xffff00

	for cube in data.lightOff
		# console.log cube.target.material.color
		cube.target.material.color.setHex 0x999999
	# console.log data.move

for el in $("#toolBar input[type=radio]")
	el.onchange = () ->
		currentMode = this.value
		Menu.hide()
		console.log currentMode
		if currentMode is 'run' and not isGaming
			isGaming = true
			console.log originBlockArray
			runInfo = runtime.startGame originBlockArray, gameCallback
			if not runInfo
				console.log runInfo
				alert 'error'
		if currentMode isnt 'run' and isGaming
			isGaming = false
			runtime.stopGame()


isConnected = (cube1, cube2) ->
	pos1 = cube1.target.position
	pos2 = cube2.target.position
	if (parseInt(Math.abs(pos1.x - pos2.x)) + parseInt(Math.abs(pos1.y - pos2.y)) +	parseInt(Math.abs(pos1.z - pos2.z)) is 50)
		return true
	return false

SURF_NUM = 
	X_POS : 0
	X_NEG : 5
	Y_POS : 1
	Y_NEG : 4
	Z_POS : 2
	Z_NEG : 3

getOpposetSurface = (surf) ->
	return 5 - surf

linkCube = (cube1, cube2) ->
	pos1 = cube1.target.position
	pos2 = cube2.target.position
	console.log pos1, pos2
	surf = -1
	if parseInt(pos1.x) isnt parseInt(pos2.x)
		surf = SURF_NUM.X_POS if pos1.x > pos2.x
		surf = SURF_NUM.X_NEG if pos1.x < pos2.x
	if parseInt(pos1.y) isnt parseInt(pos2.y)
		surf = SURF_NUM.Y_POS if pos1.y > pos2.y
		surf = SURF_NUM.Y_NEG if pos1.y < pos2.y
	if parseInt(pos1.z) isnt parseInt(pos2.z)
		surf = SURF_NUM.Z_POS if pos1.z > pos2.z
		surf = SURF_NUM.Z_NEG if pos1.z < pos2.z

	console.log surf, getOpposetSurface(surf)
	cube1.surfaces[surf].info.linkTo = cube2.surfaces[getOpposetSurface(surf)]
	cube2.surfaces[surf].info.linkTo = cube1.surfaces[getOpposetSurface(surf)]

calculateConnectionBasedPosition = (newCube) ->
	console.log newCube
	for cube in originBlockArray
		if isConnected newCube, cube
			linkCube newCube, cube

checkSurfBasedVector = (vector) ->
	if parseInt(vector.x) isnt 0
		return SURF_NUM.X_POS if vector.x > 0
		return SURF_NUM.X_NEG if vector.x < 0
	if parseInt(vector.y) isnt 0
		return SURF_NUM.Y_POS if vector.y > 0
		return SURF_NUM.Y_NEG if vector.y < 0
	if parseInt(vector.z) isnt 0
		return SURF_NUM.Z_POS if vector.z > 0
		return SURF_NUM.Z_NEG if vector.z < 0
	throw "what vector?"
